<!DOCTYPE html PUBLIC "-//w3c//dtd html 4.0 transitional//en">
<html><head>


   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <meta name="GENERATOR" content="vim">
   <meta name="Author" content="Nicolas Wolovick">
   <meta name="Keywords" content="bash UNIX shell sistemas operativos operating systems FaMAF education">
   <style type="text/css">
	H1, H2, H3, DIV, P, LI,
	UL, OL, TABLE, TD, TR {font-family: lucida sans, helvetica, arial, sans-serif;}
	pre {background: #eee; border: 1px dashed #889; margin-left: 3em; padding: 1ex;}
	dt {font-family: monospace; font-weight: bold;}
   </style><title>Lab1: Un Baash</title></head><body>

<h1><a class="autolink" title="Lab1: un Baash" href="http://educa.di.unc.edu.ar/mod/forum/view.php?id=2941">Lab1: Un Baash</a></h1>


<!-- ====================================================================  -->
<h2>Objetivos</h2>
<ol>
    <li>Utilizar los mecanismos de <b>concurrencia</b> y
        <b>comunicación</b> de <i>gruesa granularidad</i> que brinda UNIX.
    </li>
    <li>Comprender como los interpretes de línea de comando reflejan la arquitectura
        y estructura
        interna de éstas primitivas de comunicación y concurrencia.
    </li>
    <li>Implementar de manera <i>sencilla</i> un intérprete de
        línea de comandos (shell) al estilo de Bourne shell.
    </li>
</ol>


<!-- ====================================================================  -->
<h2>Introducción</h2>
<p>
La interfaz más tradicional de un sistema operativo <b>UNIX-like</b>
(*NIX) es el <i>intérprete de línea de comandos</i>. Este
programa, que ejecuta en modo usuario, funciona en cualquier *NIX que
soporte interface de caracteres y su función es aceptar comandos
ingresados por <i>entrada estándar</i> (teclado), "parsearlos"<sup><a href="#parsear">1</a></sup>, ejecutar la
orden y mostrar el resultado en la <i>salida estándar</i> (pantalla), para luego volver a repetir
el proceso.
</p><p>
Por defecto UNIX ejecuta un proceso <i>shell</i> cada vez que un usuario interactivo
ingresa al sistema. Aunque esto puede ser configurado de otra manera (ver
el último campo de cada línea del archivo <tt>/etc/passwd</tt>),
en la mayoría de los casos luego de ingresar nuestro nombre de usuario
y contraseña, el proceso que maneja el ingreso de usuarios genera
un <i>proceso hijo</i> que ejecuta un shell, con el uid/gid (identificador
de usuario y grupo) correspondiente al usuario. En este momento la pantalla
se suele presentar de la siguiente manera:

</p><pre width="75">[juan@hal juan]$
</pre>

<p>
Después de este texto inicial llamado <i>prompt</i> (que contiene
información de entorno como por ejemplo el nombre del usuario, el
nombre del host y el último tramo del directorio actual), el shell
espera datos a través de la entrada estándar <tt>stdin</tt> que normalmente se asocia al dispositivo
teclado. Podemos escribir el comando que deseamos que el shell ejecute,
e iniciar la ejecución ingresando el caracter NEWLINE <tt>'\n'</tt> generalmente
asociado con la tecla Enter o Return.

</p><pre width="75">[juan@hal juan]$ sleep 10
</pre>

<p>
Hará que el shell ejecute un proceso con el programa binario
que se encuentra en <tt>/bin/sleep</tt>, pasándole el argumento
"<tt>10</tt>".


</p><h3>Operación Básica</h3>
<p>
La <b>sintáxis</b> básica del intérprete de comandos más
usual de *NIX, conocido como Bourne shell (Bourne again shell - bash, en
Linux) es la siguiente,
</p><p>
<tt>&nbsp;&nbsp;&nbsp; comando argumento1 argumento2 ...</tt>
</p><p>
donde el comando y los argumentos son secuencias de caracteres separados
por uno o más espacios.
</p><p>
La <b>semántica</b> dice que al presionar Enter el shell buscará
el <tt>comando</tt> dentro de los <i>comandos internos</i> y si no lo encuentra tratará
de buscar un archivo ejecutable con ese nombre, siguiendo las reglas de
camino de *NIX o componiendo el comando a la secuencia de la <i>variable
de entorno</i> <tt>PATH</tt>, para luego crear un <i>proceso hijo</i> que
cargará y ejecutará el contenido de ese archivo con los argumentos
correspondientes.
</p><p>
Los <i>comandos internos</i> son manejados directamente por el shell, sin requerir
de ningún archivo externo. Un ejemplo es el comando de cambio de directorio
<tt>cd</tt>,
el cual no se encuentra como archivo ejecutable en ningún lugar
del árbol de directorio (el comando <tt>find /bin /sbin /usr/bin
/usr/sbin -perm +400 -type f -name cd</tt> no devuelve nada).
<br>Con <tt>man builtin</tt> obtenemos una lista de todos los comandos
internos implementados en <tt>bash</tt>.
</p><p>
Si el comando no es un <i>builtin</i>, el shell deberá buscar
un archivo dentro de su sistema de archivos, cargarlo y ejecutarlo pasándole
los argumentos. El problema principal es dónde encontrar este archivo.
Existen tres formas:
</p><ul>
    <li>Camino absoluto</li>
    <li>Camino relativo</li>
    <li>Búsqueda en secuencia <tt>PATH</tt></li>
</ul>
<p>
Cuando el comando comienza con <tt>/</tt>, éste se toma como un <i>camino
absoluto</i> dentro del árbol del filesystem, el shell lo cargará
en memoria y ejecutará el comando. En cambio si el comando comienza
con el nombre de un directorio, <tt>.</tt> o <tt>..</tt>, se debe seguir las
reglas usuales de <i>camino relativo</i> de *NIX, cargar y ejecutar el archivo comando,
relativo al camino actual (ver comando <tt>pwd</tt>).
</p><p>
Otro mecanismo entra en juego cuando el comando no comienza con un delimitador
de camino absoluto o relativo. La variable de entorno <tt>PATH</tt>, que
puede ser leída con el comando <tt>env</tt> o con <tt>echo $PATH</tt>,&nbsp;
sirve de secuencia de caminos absolutos o relativos, separados por '<tt>:</tt>'
que serán prefijados al comando hasta encontrar un archivo que pueda
ser leído y ejecutado.
</p><p>
Usemos el archivo ejecutable <tt>/bin/date</tt> que nos proporciona
la fecha y hora del sistema para ejemplificar los mecanismos de camino
absoluto, relativo y secuencia <tt>PATH</tt>.

</p><pre width="75">[juan@hal juan]$ /bin/date
Mon Aug 14 10:56:48 ART 2006
[juan@hal juan]$ cd /usr
[juan@hal /usr]$ ../bin/date
Mon Aug 14 10:57:02 ART 2006
[juan@hal /usr]$ cd /
[juan@hal /]$ bin/date
Mon Aug 14 10:57:15 ART 2006
[juan@hal /]$ cd ~
[juan@hal juan]$ date
Mon Aug 14 10:57:33 ART 2006
</pre>

Todos los comandos ejecutados por <tt>bash</tt> son el mismo <tt>/bin/date</tt>.

<h4>Ejercicios</h4>

<ol>
    <li>¿Cómo ejecutamos un archivo que se llama
        exactamente como un <i>builtin</i>?
    </li>
    <li>¿Por qué las recomendaciones de seguridad indican que es
        peligroso tener <tt>./</tt> en <tt>PATH</tt> al más puro
        estilo <a href="#dos"><tt>DOS</tt></a>?
    </li>
    <li>Supongamos que existen 2 comandos posibles dentro de la secuencia que
        contien <tt>PATH</tt>,
        donde el primero en la secuencia no está marcado como ejecutable
        y el segundo sí. ¿Qué hace el intérprete <tt>bash</tt>,
        ejecuta el segundo o informa que el primero no tiene permiso de ejecución?
        (incorporte esta semántica a <tt>baash</tt>)
    </li>
    <li>Indique qué sucede cuando se tiene un directorio en el <tt>pwd</tt> actual
        con el mismo nombre que un archivo ejecutable en el <tt>PATH</tt>.
        ¿Dice que no puede ejecutar un directorio o ejecuta el comando que encontró?
        (siga esta forma en <tt>baash</tt>)
    </li>
    <li>Obtenga la lógica más sencilla que unifique los tres tipos de
        búsqueda.
    </li>
    <li>¿Podemos poner directorios relativos en <tt>PATH</tt>? (haga lo propio con <tt>baash</tt>)
    </li>
</ol>


<h3>Procesos en Segundo Plano</h3>
<p>
En el paradigma básico para ejecutar un comando, el shell es el
proceso padre que crea a un hijo que cargará y ejecutará
el comando que se encuentra en el sistema de archivos (filesystem). Mientras tanto el padre
quedará esperando hasta que el proceso hijo termine, y a esto lo podemos ver ejecutando el comando <tt>sleep 30</tt>.
<br>
Ésto se puede cambiar para que el padre <b>no espere la
terminación del hijo</b>, si le agregamos al final el
símbolo '<tt>&amp;</tt>'.
<br>
Este operador, que puede ser pensado como <i>operador de
composición paralela</i>, crea la posibilidad de ejecutar
procesos de manera concurrente, es decir, si el comando que ejecutamos
termina con &amp; y demora un tiempo relativamente largo, todo lo que
iniciemos desde el shell y el shell mismo estarán
ejecutándose al mismo tiempo (paralelamente, concurrentemente).
</p><p>Esta característica resulta muy útil cuando nuestro
kernel esta corriendo sobre una arquitectura SMP (symmetric
multiprocessing) como por ejemplo las 
<a href="http://es.wikipedia.org/wiki/Intel_Core_Duo">Core Duo</a> (Intel) y
<a href="http://es.wikipedia.org/wiki/AMD_Athlon_64_X2">X2</a> (AMD),
que soporta varios microprocesadores compartiendo una
única imagen de memoria principal. Si tenemos un par de programas
que tardan mucho en calcular decimales de <i>pi</i> o de <i>e</i>,
podemos ponerlos a correr de manera que cada uno ocupe un
microprocesador, mientras utilizamos un poco de los dos micros para
jugar.

</p><pre width="75">[juan@deepblue calculosLargos]$ ./decimalesPi salidaPi.txt &amp;
[juan@deepblue calculosLargos]$ ./decimalesE salidaE.txt &amp;
[juan@deepblue calculosLargos]$ freeciv
</pre>

<p>
Existe un problema con los procesos hijos en <i>background</i>, la
entrada y salida estándar se comparten entre todos, con lo cual
tendremos salidas de pantalla entrelazadas y las entradas por teclado
serán no-determinísticamente asignadas a alguno de los
procesos que están corriendo.
<br>Este ejemplo nos muestra el lio que se puede armar.

</p><pre width="75">[juan@hal juan]$ yes "HOLA" &amp; yes "CHAU" &amp;
... ud. acaba de perder el control de esa shell ...
</pre>

<h4>Ejercicios</h4>
<ol>
    <li> Investigue cuáles son los comandos internos para manejo
         de procesos en background de <tt>bash</tt>.
    </li>
    <li> En el ejemplo de arriba el operador '<tt>&amp;</tt>' funciona como
        <b>operador de composición paralela</b>.
        ¿Cuál es el operador de composición
        <b>secuencial</b> en Bourne shell?
    </li>

    <li> Investigue cómo <tt>bash</tt> forma el árbol de
         procesos cuando ejecutamos <tt>cmd1 &amp; cmd2 &amp; cmd3 &amp; ... &amp;
         cmdN</tt>. Piense la respuesta y luego utilice <tt>pstree</tt> para
         corroborarla. Aplique los resultados de su observación a
         <tt>baash</tt>.
    </li>

    <li> Indique cuántas letras <tt>'a'</tt> debería imprimir
    el siguiente programa en "C". Generalice.
    
    <pre width="75"><a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/Ejemplos/fork_n.c">fork_n.c</a>
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;

int main(void)
{
	fork();
	fork();
	fork();
	printf("a\n");
	return 0;
}
	</pre>
    </li>
</ol>


<h3>Redirección de la Entrada/Salida</h3>
<p>
Cuando se crea un proceso, se le asignan tres identificadores de archivos
(<i>file descriptors</i>): <tt>stdin</tt>, <tt>stdout</tt> y <tt>stderr</tt>.
Si se lee desde <tt>stdin</tt>, los datos que se reciben serán dirigidos
desde el teclado al file descriptor <tt>stdin</tt>. Similarmente, los datos
escritos en <tt>stdout</tt>, <tt>stderr</tt> serán mapeados a la
pantalla de la terminal.
</p><p>
El usuario puede redefinir el <tt>stdin</tt> desde la línea de
comandos del shell si se provee al final del comando un <i>embudo de entrada</i>
'<tt>&lt;</tt>' y un nombre de archivo. Este archivo reemplazará
la corriente estándar de entrada, por lo que los valores que lea el comando
serán extraídos desde el archivo.
<br>
Similarmente podemos redefinir el file descriptor de <tt>stdout</tt>
con el <i>embudo de salida</i> '<tt>&gt;</tt>' y un nombre de archivo al final del
comando y los argumentos, para indicar que toda la salida estándar del
comando se acumule en el archivo.
</p><p>
Como ejemplo podemos recolectar estadísticas sobre el archivo que
contiene los usuarios del sistema y ponerlos en un archivo para su
posterior uso.
</p><pre width="75">[juan@hal juan]$ wc &lt; /etc/passwd &gt; passwd.cuantas.palabras
</pre>

<p>
O si nuestros programas <a href="http://en.wikipedia.org/wiki/CPU_bound">cpu-bound</a> generan sus resultados en pantalla
podemos ejecutarlos concurrentemente y mantener sus salidas separadas,
mientras las monitoreamos con el comando <tt>tail</tt>.

</p><pre width="75">[juan@deepblue juan]$ forest3d -n 0.2 &gt; n0.2.out &amp;
[juan@deepblue juan]$ forest3d -n 0.3 &gt; n0.3.out &amp;
[juan@deepblue juan]$ tail -f n0.2.out n0.3.out
</pre>

<h4>Ejercicios</h4>
<ol>
    <li> Las corrientes estándar <tt>stdout</tt> y <tt>stderr</tt> están
        dirigidas ambas a la consola. ¿Cómo podemos utilizar los
        opeardores de redirección para separarlas?
    </li>
    <li> De más <a class="autolink" title="Ejemplos" href="http://educa.di.unc.edu.ar/mod/resource/view.php?id=3117">ejemplos</a> de como usar la redirección de entrada. Tenga cuidado que muchos
        programas deshabilitan la posibilidad de redirigir la entrada estándar a otra cosa
        que no sea una terminal.
    </li>
    <li>¿Podemos redirigir de manera independiente la entrada y la salida de una secuencia
        de comandos separados por <tt>&amp;</tt>? ¿Qué sucede con la composición
        secuencial <tt>;</tt>? (piense en este comportamiento en caso de que realice los puntos
        complementarios).
    </li>
</ol>

<h3>Tuberías (o ... la plomería continúa)</h3>
<p>
Las tuberías (pipes) son el principal mecanismo de Comunicación
entre Procesos (IPC) para *NIX. 
</p><p>
Una tubería se compone de dos puntas,
una entrada y una salida, cada una conectada a un proceso. Las puntas de
una tubería son un par de <i>filedescriptors</i>, uno para escribir
y el otro para leer. Un pipe es básicamente un buffer FIFO, donde
tenemos un <b>productor</b>, sobre el <i>write-end</i> y un <b>consumidor</b>
sobre el <i>read-end</i>. Como sabemos que todas las colas que se encuentran
en el sistema operativo son acotadas (memoria finita), además de
tener un read bloqueante cuando no hay datos para consumir, tenemos un
send bloqueante cuando el buffer está lleno.

</p><p>
El shell provee la funcionalidad de un pipe a través del operador
'<tt>|</tt>', denominado justamente <b>pipe</b>, el cual conecta la salida
estándar del proceso lanzado por el comando de la izquierda del pipe, con
la entrada estándar del proceso que se genera con el comando a la derecha
del símbolo de tubería.
</p><p>
Las tuberías se pueden componer secuencialmente para generar
una <i>línea de producción de datos</i>, el cual es un estilo arquitectural
denominado <i>pipes&amp;filters</i>.
</p><p>
Estos <a class="autolink" title="Ejemplos" href="http://educa.di.unc.edu.ar/mod/resource/view.php?id=3117">ejemplos</a> cuentan cuantas veces Juan ingresó al sistema,
los pids (process identifiers) de todos los navegadores <a href="http://www.mozilla-europe.org/es/">Firefox</a>, y el listado ordenado
de mayor a menor de todos los usuarios del sistema que tienen <tt>bash</tt>.

</p><pre width="75">[juan@hal juan]$ last juan | wc -l
[juan@hal juan]$ ps aux | grep firefox-bin
[juan@hal juan]$ grep bash /etc/passwd | cut -d ":" -f 1 | sort -r
</pre>

<p>
El siguiente ejemplo es un poco más complicado y nos permite ver cuales subdirectorios de nuestro "home" ocupan más.

</p><pre width="75">[juan@hal juan]$ find ~/ -maxdepth 1 -type d | xargs du -s | sort -n -r
</pre>

<h4>Ejercicios</h4>
<ol>
    <li>¿Cúal es el mecanismo para que este estilo <i>pipes&amp;filters</i>,
        hasta ahora absolutamente lineal, permita bifurcaciones?
	<br>Si tuvieramos bifurcaciones (o "T's" para un plomero), podríamos capturar la salida
        estándar en un archivo y al mismo tiempo visualizarla en consola.
    </li>
    <li>Los pipes existen en el filesystem mientras dura el comando.
        ¿En que parte del árbol de directorios se encuentran y qué atributos tienen?
    </li>
    <li>¿Cómo es el pipe de comandos para generar el <i>dump</i>
        de nivel 0 que se reestablece interactivamente con el ejemplo anterior?
    </li>
    <li>Muestre el <tt>uid</tt> máximo en su sistema NIS (<tt>ypcat passwd</tt>
        realiza el trabajo básico).
    </li>
    <li>Muestre todos los login repetidos de su sistema NIS.
    </li>
</ol>


<!-- ====================================================================  -->
<h2>Introducción a las llamadas de sistema (syscall)</h2>

El shell en su funcionalidad básica es un <b>reflejo exacto</b> de cierto subconjunto de syscalls del kernel de UNIX:
<ul>
	<li><tt>fork</tt>: crea proceso hijo, clon del padre.</li>
	<li><tt>execv</tt>: reemplaza la imagen en memoria de un proceso por el de un archivo del filesystem.</li>
	<li><tt>wait</tt>: espera la terminación de un hijo.</li>
	<li><tt>dup</tt>: duplica una entrada de los descriptores de archivos (file descriptors, FD).</li>
	<li><tt>pipe</tt>: crea una tubería.</li>
</ul>

<p>
Esto se puede ver de manera <b>extremadamente clara</b> en los
capítulos V y VI del paper que presentó el sistema operativo UNIX en
1974/78 y en el 2003 y 2004 se trabajó sobre una traducción:
</p><ul>
    <li> D. M. Ritchie and K. Thompson,
    "<em>The UNIX Time-Sharing System</em>"
    [<a href="http://cm.bell-labs.com/cm/cs/who/dmr/cacm.html">html</a>]
    [<a href="http://cm.bell-labs.com/cm/cs/who/dmr/cacm.pdf">pdf</a>]
    </li>
    <li> Traducción al español por Alini, Bianchi y Bordese,
    "<em>El Sistema de Tiempo Compartido UNIX</em>"
    [<a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/unixtss.sp.html">html</a>]
    </li>
</ul>

<p>
Veamos un ejemplo de uso del <tt>fork</tt> y <tt>wait</tt> para crear
toda una parentela de procesos. Notar el interleaving que se produce en
las ejecuciones.

</p><pre width="100"><a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/Ejemplos/fork.c">fork.c</a>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/wait.h&gt;
#include &lt;sys/types.h&gt;	/* para tener pid_t */

#define N 8

int main(int argc, char **argv)
{
	unsigned int i, j, h ;
	pid_t s;
	int status;

	if (argc != 2) {
		fprintf(stderr, "Uso: %s cantidad_de_hijos\n", argv[0]);
		exit(1);
	}
	h = atoi(argv[1]);

	for (i = 0; i &lt; h; i++) {
		s = fork();
		if (s &lt; 0) {
			perror("Creando el hijo");
			exit(1);
		} else if (s == 0) {
			for (j = 0; j &lt; N; j++) {
				printf("Hijo pid=%5d: iteracion %3d\n", getpid(), j);
			}
			return 0;  /*no quiero nietos! */
		} else {
			printf("PADRE: hijo pid=%5d, creado\n", s);
		}
	}

	for (i = 0; i &lt; h; i++) {
		s = wait(&amp;status);
		if (s &lt; 0) {
			perror("Esperando el hijo");
			exit(1);
		}
		printf("PADRE: hijo pid=%5d, terminado\n", s);
	}
	return 0;
}
&lt;%23.46%&gt;

<p>
El otro <i>syscall</i> a utilizar es <tt>execv</tt>, el cual reemplaza
la imagen del proceso por una nueva que se encuentra en un archivo. En
este pequeño ejemplo, se reemplaza el proceso actual por el que
ejecuta el programa
<tt>date</tt>.

</p><pre width="75"><a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/Ejemplos/execvp.c">execvp.c</a>
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;

int main(void)
{
	execvp("date", NULL);
	printf("Llega hasta aca?\n");
	return 0;
}
</pre>

<p>
Aunque el syscall <tt>execvp</tt>, parece que le soluciona todos
los problemas relativos a la búsqueda el fullpath del ejecutable,
<b>no se permite su utilización</b> en este proyecto y deberán codificar o reutilizar
algoritmos y estructuras de datos apropiadas.

<!-- hay un bug o algo en Firefox 2.0.0.5, esto <%11.10%>utilización</b> renderea "utilizacíon"
-->

</p><p>
Con estos syscalls, tenemos la funcionalidad básica de un shell, la de poder <b>ejecutar comandos en un proceso hijo completamente aislado</b>.

</p><p>
Para poder <b>correr concurrentemente</b> el shell con el proceso hijo creado para ejecutar el comando, es decir cuando hay un operador '<tt>&amp;</tt>' a la derecha de todos los argumentos, simplemente no hay que esperar!

</p><p>
Cuando se crea un proceso, este hereda de los descriptores de archivos
abiertos de su padre. Además como el proceso 1, denominado <tt>init</tt>
y padre de los restantes procesos&nbsp; del sistema (<tt>ps aux | grep
init</tt>, o bien <tt>pstree</tt> para obtener el árbol genealógico),
tiene abierto los tres primeros identificadores de archivos <tt>stdin</tt>,
<tt>stdout</tt>
y <tt>stderr</tt> (0,1 y 2) a descriptores de archivos asociados a la terminal
de entrada y a la terminal de salida; todos los hijos heredan estos fileid's
asociados a los file descriptors.
</p><p>
Para <b>implementar los operadores de redirección</b> el shell cambia los filedescriptors asociados a los fileid's de <tt>stdin</tt>, <tt>stdout</tt> y <tt>stderr</tt>.
Esto se puede hacer con la syscall <tt>dup</tt>, que duplica
el filedescriptor indicado en su argumento, en el menor fileid libre. Veamos
un ejemplo que redirige la salida a un archivo. Notar que las syscall&nbsp;
para manejo de archivos que no son las habituales de la <a href="http://www.gnu.org/software/libc/">libc</a> (<tt>fopen</tt>, <tt>fclose</tt>, ...).

</p><pre width="100"><a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/Ejemplos/dup.c">dup.c</a>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;

#define STDOUT_FID 1

int main(void)
{
	int fid;
	int flags, perm;

	flags = O_WRONLY | O_CREAT | O_TRUNC;
	perm = S_IWUSR | S_IRUSR;
	fid = open("redireccion.out", flags, perm);
	if (fid &lt; 0) {
		perror("open");
		exit(1);
	}
	close(STDOUT_FID);		/* stdout def. en stdio.h es un FILE* */
	if (dup(fid) &lt; 0) {
		perror("dup");
		exit(1);
	}

	printf("Hello World!\n");
	close(fid);

	return 0;
}
&lt;%23.46%&gt;

<p>
Para implementar el operador de tubería '<tt>|</tt>', el shell utiliza
la syscall <tt>pipe</tt>, la cual crea en el espacio del filesystem
un nuevo nodo de este tipo y devuelve dos fileid's que serán la
salida y la entrada de la tubería.
<br>Veamos un sencillo ejemplo de IPC.

</p><pre width="100"><a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/Ejemplos/pipe.c">pipe.c</a>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/wait.h&gt;
#include &lt;sys/types.h&gt;

int main(void)
{
	pid_t fork_id;
	int srv_client_pipe[2];
	int client_srv_pipe[2];

	if (pipe(srv_client_pipe) &lt; 0) {
		perror("srv_client_pipe");
		exit(1);
	}
	if (pipe(client_srv_pipe) &lt; 0) {
		perror("client_srv_pipe");
		exit(1);
	}
	if ((fork_id = fork()) &lt; 0) {
		perror("fork");
		exit(1);
	};

	if (fork_id != 0) {  /* soy padre, soy cliente */
		int status;
		int x, fx;

		x = 8;
		printf("CLIENTE: envia %d\n", x);
		write(client_srv_pipe[1], &amp;x, sizeof(int));
		read(srv_client_pipe[0], &amp;fx, sizeof(int));
		printf("CLIENTE: recibe %d\n", fx);
		wait(&amp;status);
		close(srv_client_pipe[0]);
		close(srv_client_pipe[1]);
		close(client_srv_pipe[0]);
		close(client_srv_pipe[1]);
	} else {  /* soy hijo, soy server */
		int in, out;
		read(client_srv_pipe[0], &amp;in, sizeof(int));
		printf("SERVIDOR: recibe %d\n", in);
		out = in * in;
		printf("SERVIDOR: envia %d\n", out);
		write(srv_client_pipe[1], &amp;out, sizeof(int));
	}

	return 0;
}
&lt;%23.46%&gt;

<p>
Notar que este programa aunque concurrente, presenta sólo un
interleaving en su salida. Se dice que el programa está <i>fuertemente
sincronizado</i> debido a las primitivas de comunicación.

</p><p>
Como se puede ver todos los elementos escenciales para la construcción de un baash están dados por estas 5 system calls.


<!-- ====================================================================  -->
</p><h2>Objetivos de implementación</h2>

<p>
Codificar un Bourne Again Again SHell (baash) que tenga las siguientes funcionalidades generales:
</p><ul>
	<li>Ejecución de comandos en espera y concurrente pasando los parámetros correspondientes.
	</li>
	<li>Redirección de entrada y salida.
	</li>
	<li>Pipe entre comandos.
	</li>
</ul>
<p>
Debería poder ejecutar correctamente los siguientes <a class="autolink" title="Ejemplos" href="http://educa.di.unc.edu.ar/mod/resource/view.php?id=3117">ejemplos</a>:
</p><ul>
	<li><tt>ls -l baash.c</tt></li>
	<li><tt>ls 1 2 3 4 5 6 7 8 9 10 11 12 ... 194</tt></li>
	<li><tt>wc -l &gt; out &lt; in&lt;%21.42%&gt;</tt></li>
<tt>	</tt><li><tt><tt>xeyes &amp;</tt></tt></li>
<tt>	</tt><li><tt><tt>ls | wc</tt></tt></li>
</ul>
<p>
<tt>En particular deberán:
</tt></p><ul>
<tt>	</tt><li><tt>Implementar los comandos internos <tt>exit</tt> y <tt>cd</tt> (ver la syscall <tt>chdir</tt>).</tt></li>
<tt>	</tt><li><tt>Realizar la búsqueda de camino, si el camino no es absoluto o relativo, según la variable de entorno <tt>PATH</tt>. Por lo tanto no podrán utilizar <tt>execvp</tt>.</tt></li>
<tt>	</tt><li><tt>Imprimir un <i>prompt</i> con los información relevante, como por ejemplo nombre del host, nombre de usuario y camino relativo.</tt></li>
<tt>	</tt><li><tt>Poder salir con CTRL-D, el caracter de fin de transmisión (EOT).</tt></li>
<tt>	</tt><li><tt>El baash debe aceptar entrada redirigida, es decir <tt>echo -en "ls\nexit\n" | ./baash</tt> tiene que listar el directorio actual y salir.</tt></li>
</ul>
<p>
<tt>Para la implementación se pide:
</tt></p><ul>
<tt>	</tt><li><tt>Utilizar TAD opacos.</tt></li>
<tt>	</tt><li><tt>No perder memoria, salvo casos debidamente reportados en el informe.</tt></li>
<tt>	</tt><li><tt>Seguir un estilo de codificación.</tt></li>
<tt>	</tt><li><tt>Utilizar todos las ideas de programación defensiva presentadas en clase.</tt></li>
<tt>	</tt><ul>
<tt>		</tt><li><tt>En "<a href="http://www.advancedlinuxprogramming.com/">Advanced
Linux Programming</a>", en su Capítulo 2 "<a href="http://www.advancedlinuxprogramming.com/alp-folder/alp-ch02-writing-good-gnu-linux-software.pdf">Escribiendo buenos programas GNU/Linux</a>" muestra le uso del <tt>assert</tt> en la página
30, y como manejar correctamente los errores que pueden devolver las llamadas
a sistema en la página 33. Estudien este material antes de comenzar
la codificación. Los <a class="autolink" title="Ejemplos" href="http://educa.di.unc.edu.ar/mod/resource/view.php?id=3117">ejemplos</a> de código contienen un poco
de <i>programación defensiva</i>.
		</tt></li>
<tt>	</tt></ul>
</ul>

<!-- ====================================================================  -->
<h2><tt>Como atacar el problema</tt></h2>

<!-- ....................................................................  -->
<h3><tt>Tokenizer</tt></h3>

<tt>La tarea más básica que se debe realizar consiste en partir la cadena de caracteres de entradas en palabras para luego poder tomar de ella el comando, sus argumentos además de las redirecciones, tuberías y procesos en segundo plano.
</tt><p>
<tt>Esta es la fase inicial de un <i>parsing</i>.
</tt></p><p>
<tt>Para esto se proveen dos TAD opaco que implementa un <i>iterador de palabras sobre strings</i>:
</tt></p><ul>
<li><tt> El TAD <code>Lexer</code> utilizado en el Lab0.
</tt></li>
<li><tt>Otro denominado <tt>string_tokenizer</tt>, que sigue la filosofía de <a href="http://java.sun.com/javase/6/docs/api/java/util/StringTokenizer.html">StringTokenizer</a> de Java.
</tt></li>
</ul>
<p>
<tt>Ustedes deberán elegir alguno de los dos y utilizarlo en la fase inicial del procesamiento del comando.
</tt></p><p>

<!-- ....................................................................  -->
</p><h3><tt>Modularización Funcional</tt></h3>

<tt>Se propone una división en 3 módulos funcionales:
</tt><ul>
<tt>	</tt><li><tt>Parser</tt></li>
<tt>	</tt><li><tt>Ajuste de Camino</tt></li>
<tt>	</tt><li><tt>Ejecutor</tt></li>
</ul>

<p>
<tt>El <b>parser</b> toma un string y devuelve un TAD con toda la información interpretada de la secuencia de caracteres.
El <b>ajuste de camino</b> toma el TAD anterior y adapta el camino del/de los comandos del filesystem para obtener a su salida solo caminos absolutos o relativos.
</tt></p><p>
<tt>Finalmente el <b>ejecutor</b> toma el TAD y procede a realizar los <tt>{fork, execv, wait, dup, pipe}</tt> necesarios.
</tt></p><p>
<tt>Esta modularización funcional depende fuertemente del TAD que se defina para la comunicación entre los módulos.

<!-- ....................................................................  -->
</tt></p><h3><tt>TAD <code>pipeline</code> de comunicación entre módulos</tt></h3>

<tt>A partir de la lectura de <tt>man bash</tt> en su sección <tt>SHELL GRAMMAR</tt>, se extrae la gramática que entiende el shell.
Esta se divide en 3 capas: comando simple, tubería y lista, en orden creciente de complejidad.
Limitaremos la implementación a los 2 primeros niveles.
</tt><ul>
<tt>	</tt><li><tt>Un <b>comando simple</b> es una secuencia de palabras donde la primera es el comando y las subsiguientes sus argumentos. Resultan opcionales dos redirectores, uno de entrada y otro de salida.</tt></li>
<tt>	</tt><li><tt>Una <b>tubería</b> es una secuencia de comandos simples conectados por pipe con un terminador que indica si la ejecución será en segundo plano.</tt></li>
</ul>

<table summary="" border="1" cellpadding="3" cellspacing="0" width="100%">
	<tbody><tr><td align="center"><b>Nombre</b></td><td align="center"><b>Ejemplo</b></td><td align="center"><b>Definición estilo Haskell</b></td></tr>
	<tr><td><code>scommand</code></td> <td><tt>ls -l ej1.c &gt; out &lt; in&lt;%21.42%&gt;</tt></td> <td><code>([String],String,String)</code></td> </tr>
	<tr><td><code>pipeline</code></td> <td><tt>ls -l *.c &gt; out &lt; in  |  wc  |  grep -i glibc &amp;&lt;%21.42%&gt;</tt></td> <td><code>([scommand],Bool)</code></td> </tr>
</tbody></table>


<h4><tt><a class="autolink" title="Ejemplos" href="http://educa.di.unc.edu.ar/mod/resource/view.php?id=3117">Ejemplos</a></tt></h4>

<table summary="" border="1" cellpadding="1" cellspacing="0" width="100%">
	<tbody><tr><td align="center"><b>Comando</b></td><td align="center"><b>Estructura</b></td><td align="center"><b>Comentario</b></td></tr>
	<tr><td><code>ls -l </code></td> <td><code>( [(["ls", "-l"], "", "")], False )</code></td><td><font size="-1">Sin ejecución en 2do plano, un elemento de pipeline.<br>Este único elemento tiene 2 cadenas y sin redirectores de entrada y salida.</font></td></tr>
	<tr><td><code>xeyes &amp;</code></td> <td><code>( [(["xeyes"],"","")], True )</code></td> <td><font size="-1">Un elemento en el pipeline con ejecución en 2do plano.<br>Ese elemento no tiene redirectores ni argumentos.</font></td></tr>
	<tr><td><code>ls | wc -l</code></td> <td><code>( [(["ls"],"",""),(["wc", "-l"],"","")], False )</code></td> <td><font size="-1">Sin ejecución en 2do plano, dos comandos simples en pipeline.</font></td></tr>
	<tr><td><code>ls -l ej1.c &gt; out &lt; in&lt;%42.576%&gt;</code></td> <td><code>( [(["ls","-l","ej1.c"],"out","in")], False )</code></td> <td><font size="-1">Vemos como el único comando simple del pipeline tiene redirectores.</font></td></tr>
	<tr><td><code>ls -l *.c &gt; out &lt; in  |  wc  |  grep -i glibc &amp;&lt;%42.576%&gt;</code></td> <td><code>( [scomm0,scomm1,scomm2], True )<br>where<br>scomm0=(["ls", "-l", "ej1.c"],"out","in")<br>scomm1=(["wc"],"","")<br>scomm2=(["grep","-i","glibc"],"","")</code></td> <td><font size="-1">La estructura usada al máximo.</font></td></tr>
</tbody></table>

<p>
<tt>Se propone un encabezado (header) para un TAD que contenga esta información.

</tt></p><pre><tt><a href="http://educa.di.unc.edu.ar/file.php/142/Lab1/command.h">command.h</a>
#ifndef __COMMAND_H
#define __COMMAND_H

#include &lt;stdbool.h&gt; // para tener bool


/* A partir de man bash, en su seccion de SHELL GRAMMAR,
   se diseñó el siguiente TAD.
   scommand: comando simple, es una secuencia de cadenas
	(comando y argumentos), además de dos posibles redirectores.
   pipeline: una secuencia de comandos simples que se ejecutarán
	en un pipeline, se indica tambien si hay que esperar
	o continuar.
*/


// Comando simple, por ejemplo: ls -l ej1.c &gt; out &lt; in

typedef struct scommand_s scommand_s;

scommand_s *scommand_create(void);
void scommand_destroy(scommand_s *sc);
// los modificadores y proyectores manejan la memoria pedida externamente
void scommand_push_back(scommand_s *sc, const char *str);
void scommand_push_front(scommand_s *sc, const char *str);
char *scommand_pop_front(scommand_s *sc);
bool scommand_is_empty(const scommand_s *sc);
// lo mismo para redirectores
void scommand_set_redir_in(scommand_s *sc, const char *str);
char *scommand_get_redir_in(const scommand_s *sc);
void scommand_set_redir_out(scommand_s *sc, const char *str);
char *scommand_get_redir_out(const scommand_s *sc);
// preety printer para hacer debugging/logging
char *scommand_to_string(const scommand_s *sc); // ver asprintf, snprintf


// Tuberias, por ejemplo: ls -l *.c &gt; out &lt; in  |  wc  |  grep -i glibc  &amp;

typedef struct pipeline_s pipeline_s;

pipeline_s *pipeline_create(void);
void pipeline_destroy(pipeline_s *ps);
// los modificadores y proyectores manejan la memoria pedida externamente
void pipeline_push_back(pipeline_s *ps, const scommand_s *sc);
scommand_s *pipeline_pop_front(pipeline_s *ps);
bool pipeline_is_empty(const pipeline_s *ps);
// no hay memoria para manejar en esta parte, bool es un tipo básico
void pipeline_set_wait(pipeline_s *ps, const bool w);
bool pipeline_get_wait(const pipeline_s *ps);
// preety printer para hacer debugging/logging
char *pipeline_to_string(const pipeline_s *ps); // ver asprintf, snprintf


#endif /* __COMMAND_H */
&lt;%23.46%&gt;

Notar que para cumplir con la funcionalidad pedida alcanza con implementar <tt>scommand</tt> completo y <tt>pipeline</tt> para almacenar hasta 2 elementos.



<!-- ....................................................................  -->
</tt><h3><tt>Algunos consejos</tt></h3>
<ul>
<tt>	</tt><li><tt>Antes que nada definir el TAD que será la interface de comunicación entre los 3 módulos propuestos.</tt></li>
<tt>	</tt><li><tt>La generalidad de la estructura propuesta puede resultar compleja. Una estrategia es definir sólo el TAD <tt>scommand</tt> e implementar los 3 módulos funcionales, una vez listo y probado avanzar a <tt>pipeline</tt>.</tt></li>
<tt>	</tt><li><tt><b>Dividir</b> la tarea de codificación para que cada integrante del grupo tome un módulo funcional, luego si cada uno hizo bien su trabajo, el proyecto estará concluido al ensablar y ajustar las 3 partes.</tt></li>
<tt>	</tt><li><tt>Codificar rápidamente el lazo principal de entrada-parseo-ajustepath-ejecución con <a href="http://en.wikipedia.org/wiki/Method_stub">stubs</a> en todas las rutinas, para tener una base sobre la cual ir codificando y probando de manera interactiva.</tt></li>
<tt>	</tt><li><tt>Testear la implementación de manera exhaustiva.</tt></li>
<tt>	</tt><li><tt>Recordar que el shell refleja tan bien las 5 syscalls que la el módulo de ejecución deberá ser muy compacto. En caso contrario reorganice sus ideas para lograr este objetivo.</tt></li>
<tt>	</tt><li><tt>Las estructuras de datos contienen muchas listas. Si se tiene a mano alguna implementación genérica de listas, se podrá ahorrar bastante trabajo.</tt></li>
</ul>


<!-- ====================================================================  -->
<h2><tt>Tareas adicionales</tt></h2>

<tt>Se pueden hacer las siguientes mejoras:
</tt><ul>
<tt>    </tt><li><tt>Implementar toda la generalidad para aceptar la gramática de <tt>list</tt> según la sección <code>SHELL GRAMMAR</code> de <code>man bash</code>.
    	Por ejemplo se podrá ejecutar <code>ls -l | wc ; ls &amp; ps</code>.
    	Para hacer esto habrá que pensar mejor las estructuras porque <code>pipeline</code> incorpora el indicador de 2do plano que debería estar en <code>list</code>.
    </tt></li>
<tt>    </tt><li><tt>Manejar bien los procesos lanzados en background para que no se generen <i>zombies</i>.
        Pueden consultar la sección 3.4.3 de 
        "<a href="http://www.advancedlinuxprogramming.com/">Advanced Linux Programming</a>",
        que está en la página 57.
    </tt></li>
<tt>    </tt><li><tt>Prompt configurable desde la variable de entorno <tt>PS1</tt>.
    </tt></li>
</ul>


<!-- ====================================================================  -->
<h2><tt>Material adicional</tt></h2>
<ul>
<tt>    </tt><li><tt>"<a href="http://www.faqs.org/docs/artu/ch07s02.html#plumbing">Pipes, Redirection, and Filters</a>"
        del libro "<em>The Art of Unix Programming</em>".
    </tt></li>
<tt>    </tt><li><tt>Capítulo 3 (<a href="http://www.advancedlinuxprogramming.com/alp-folder/alp-ch03-processes.pdf">Process</a>) y Capítulo 5 (<a href="http://www.advancedlinuxprogramming.com/alp-folder/alp-ch05-ipc.pdf">Interprocess Communication</a>) del libro
        "<em>Advanced Linux Programming</em>".
    </tt></li>
<tt>    </tt><li><tt>Capítulo "<a href="http://www.gnu.org/software/libc/manual/html_node/Implementing-a-Shell.html">Implementing a Job Control Shell</a>", de 
        "<em>The GNU C Library Reference Manual</em>".
    </tt></li>
</ul>


<!--

<%4.3%>Tips</h1>
El problema de la b&uacute;squeda en la secuencia PATH es un simple problema
de b&uacute;squeda de secuencia, que con una buen TAD secuencia implementado
sobre listas se deber&iacute;a resolver de manera compacta.
<p>
Planteen el problema de parseo de forma general, ya saben todas las
posibilidades que hay para parsear, asi que no deber&iacute;a ser dif&iacute;cil
realizar una funcion parse_command que devuelva una estructura command
que contega toda la informaci&oacute;n necesaria para ejecutar el o los
comandos, y aplicar los operadores.
<br>
Si es lo suficientemente general, los puntos adicionales saldr&aacute;n
sin esfuerzo adicional.


<p>
Para ver cuanto se sigue la programaci&oacute;n defensiva en la
pr&aacute;ctica usual de desarrollo de software vayan a <a
href=http://freshmeat.net>freshmeat.net</a>, busquen proyectos
cualquiera en C, con fuentes disponibles, bajen la &uacute;ltima
versi&oacute;n, y encontrar en los fuentes una syscall de la cual se
ignora el resultado.<br>
Miren el c&oacute;digo de OpenSSH. Qu&eacute; notan?<br>
Este tip est&aacute; tomando la forma de ejercicio obligatorio y debe
comentarse en la documentaci&oacute;n del laboratorio.
-->

<hr>

<!-- ====================================================================  -->
<h2><tt>Notas</tt></h2>
<ul>
<tt>	</tt><li><tt><a name="parsear"></a><a href="http://en.wikipedia.org/wiki/Parse">To parse</a> es la acción de analizar una secuencia para obtener su <i>estructura gramática</i> dada por una gramática formal.
	</tt></li>
<tt>	</tt><li><tt><a name="dos"></a>DOS es Disk Operating System, el sistema operativo de los 80's que inició el desarrollo de Microsoft.</tt></li>
</ul>

<!-- footer -->
<hr>
<div align="right"><tt><font size="-1"><em>
$Date: 2007-08-19 09:25:59 -0300 (Sun, 19 Aug 2007) $,
$Revision: 203 $
</em></font>
</tt></div>


</pre></pre></pre></pre></body></html>